﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Toto.Telas;

namespace Toto.Estado
{
    /// <summary>
    /// Classe que gerencia as telas do jogo
    /// </summary>
    public class ScreenManager : DrawableGameComponent
    {
        #region [ Fields ]

        /// <summary>
        /// Armazena uma referência do jogo que o chamou
        /// </summary>
        private Game1 game;

        /// <summary>
        /// Tela atual do jogo
        /// </summary>
        private GameState currentScreen;

        /// <summary>
        /// Próxima tela a ser trocada
        /// </summary>
        private GameState nextScreen;

        /// <summary>
        /// Dicionário de telas
        /// </summary>
        private Dictionary<GameState, GameScreen> screens;

        /// <summary>
        /// Verifica se tem transição para acontecer
        /// </summary>
        private bool transition;

        /// <summary>
        /// Alpha da tela
        /// </summary>
        private float alpha = 1.0f;

        /// <summary>
        /// Imagem para fazer a transição
        /// </summary>
        private Texture2D blankTexture;

        /// <summary>
        /// Referência para o sprite batch do jogo
        /// </summary>
        private SpriteBatch spriteBatch;

        #endregion

        #region [ Constructor]
        /// <summary>
        /// Construtor do gerenciador de cenas
        /// </summary>
        /// <param name="game"></param>
        public ScreenManager(Game1 game)
            : base(game)
        {
            this.game = game;
            // Recebe o serviço de sprite batch do jogo
            this.spriteBatch = (SpriteBatch)game.Services.GetService(typeof(SpriteBatch));
            // Carrega a textura que vai servir para fazer o efeito de transição
            this.blankTexture = game.Content.Load<Texture2D>(@"blank");
            // Define a cena atual como vazia
            this.currentScreen = GameState.None;
            // Define a próxima cena como vazia
            this.nextScreen = GameState.None;
            // Define a transição como verdadeira
            this.transition = false;
            // Inicializa o dicionário de cenas
            this.screens = new Dictionary<GameState, GameScreen>();
            // Troca a cena vazia pela cena de introdução
            ChangeScreen(GameState.Intro);
        }

        #endregion

        #region [ Update and Draw ]
        /// <summary>
        /// Atualiza a tela
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
            // Caso não tenha transição 
            if (!transition)
            {
                // Atualiza a cena atual
                screens[currentScreen].Update(gameTime);
            }
            else
            {
                // Se tem transição e a cena atual é diferente de vazia
                if (currentScreen != GameState.None)
                {
                    // Aumenta o alpha conforme o tempo, ou seja,
                    // aumenta o preto na tela (fade-out)
                    // a imagem começa a desaparecer
                    alpha += (float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.001f;
                    if (alpha >= 1.0f)
                    {
                        // Mantem o alpha entre 0 e 1
                        MathHelper.Clamp(alpha, 0, 1);
                        // Remove a cena anterior (atual até o momento)
                        screens.Remove(currentScreen);
                        // Cena atual recebe estado vazio
                        currentScreen = GameState.None;
                    }
                }
                else
                {
                    if (alpha > 0.0f)
                    {
                        // Diminui o alpha conforme o tempo, ou seja,
                        // diminui o preto na tela (fade-in)
                        // a imagem começa a aparecer
                        alpha -= (float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.001f;
                    }
                    else
                    {
                        // Mantem o alpha entre 0 e 1
                        MathHelper.Clamp(alpha, 0, 1);
                        // Cena atual recebe o próximo estado
                        currentScreen = nextScreen;
                        // Acaba a transição
                        transition = false;
                    }
                }
            }
        }

        /// <summary>
        /// Desenha a cena
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);
            spriteBatch.Begin();
            // Se cena atual for diferente de vazio
            // desenha a cena atual
            // senão desenha a próxima cena
            if (currentScreen != GameState.None)
                screens[currentScreen].Draw(gameTime);
            else
                screens[nextScreen].Draw(gameTime);

            // Se existir transição aplica o efeito de fade-in e fade-out
            if (transition)
                FadeBackBufferToBlack();
            spriteBatch.End();
        }

        #endregion

        #region [ Change Screen ]

        /// <summary>
        /// Troca as cenas do jogo
        /// </summary>
        /// <param name="nextScene"></param>
        public void ChangeScreen(GameState nextScreen)
        {
            // Guarda a próxima cena
            this.nextScreen = nextScreen;
            // Define que agora existe uma transição de cenas
            this.transition = true;
            // Caso a cena seja alguma especificada realiza a inserção da mesma no dicionário
            switch (nextScreen)
            {
                case GameState.Intro:
                    // Adiciona a tela de introdução ao dicionário
                    screens.Add(GameState.Intro, new IntroScene(game));
                    break;

                case GameState.Menu:
                    // Adiciona a tela de jogo ao dicionário

                    this.screens.Add(GameState.Menu, new MenuScreen(game));
                   // MediaPlayer.Play(Game1.music1);
                   // MediaPlayer.IsRepeating = true;
                    break;

                case GameState.Gameplay:
                    // Adiciona a tela de créditos ao dicionário
                    this.screens.Add(GameState.Gameplay, new GameScene(game));
                    break;

                case GameState.Opcoes:
                    // Adiciona a tela de créditos ao dicionário
                    this.screens.Add(GameState.Opcoes, new OptionsScene(game));
                    break;

                case GameState.Credits:
                    // Adiciona a tela de créditos ao dicionário
                    this.screens.Add(GameState.Credits, new CreditsScreen(game));
                    break;

                case GameState.Exit:
                    Game.Exit();
                    break;
            }
        }

        #endregion

        #region [ Fade back buffer to black ]

        /// <summary>
        /// Desenha um sprite translúcido preto, usado para telas de fading in e out,
        /// e para escurecer o fundo atrás das popups.
        /// </summary>
        private void FadeBackBufferToBlack()
        {
            // Cria uma nova janela de visualização, conforme a janela atual do jogo
            Viewport viewport = game.GraphicsDevice.Viewport;
            // Desenha uma imagem preta com o alpha definido
            spriteBatch.Draw(blankTexture,
                             new Rectangle(0, 0, viewport.Width, viewport.Height),
                             new Color(0, 0, 0, alpha));
        }

        #endregion
    }
}
